import pygame, tiledtmxloader, os, math, sys
from classes.ani_sprites import Hero

def map_renderer(filex, resolution, screen):
  '''
  Loads maps and renders them using tiledtmxloader
  '''
  world_map = tiledtmxloader.tmxreader.TileMapParser().parse_decode(filex)

  # load the images using pygame
  resources = tiledtmxloader.helperspygame.ResourceLoaderPygame()
  resources.load(world_map)

  # make sure the map type is supported
  assert world_map.orientation == "orthogonal"

  # renderer
  renderer = tiledtmxloader.helperspygame.RendererPygame()

  # create the player
  player = Hero(os.path.join('resources','graphics','sprites','zombie.png')\
      , 24,24, resolution)

  # cam_offset is for scrolling
  cam_world_pos_x = player.rect.centerx
  cam_world_pos_y = player.rect.centery

  # set initial cam position and size
  renderer.set_camera_position_and_size(cam_world_pos_x, cam_world_pos_y, \
                                        resolution[0], resolution[1])

  # retrieve the layers
  sprite_layers = tiledtmxloader.helperspygame.get_layers_from_map(resources)

  # filter layers
  sprite_layers = [layer for layer in sprite_layers if not layer.is_object_group]
  # add the hero the the right layer
  sprite_layers[1].add_sprite(player)

  #rendering/event loop
  clock = pygame.time.Clock()
  FPS = 60
  rendering = True
  
  while rendering:
    for event in pygame.event.get():
      if event.type == pygame.QUIT:
        pygame.quit()
        sys.exit()

    #rendering code
    
    #update hero postion/check for collisions
    player.update(pygame.time.get_ticks())
    movex, movey = player.move
    #get hero's location
    tempx, tempy = player.rect.midbottom
    temp = [tempx, tempy]
    temptemp = temp

    step_x, step_y = check_collision(tempx, tempy, movex, movey, 24, 24, sprite_layers[3])
    
    temp[0] += step_x
    temp[1] += step_y
    #make sure there is actually movement if positive 
    if step_x > 0:
      tt = temp[0] - temptemp[0]
      temp[0] = temptemp[0] + 1 + tt
    if step_y > 0:
      tt = temp[1] - temptemp[1]
      temp[1] = temptemp[1] + 1 + tt

    player.rect.midbottom = temp 

    #set camera to player
    renderer.set_camera_position(player.rect.centerx, player.rect.centery)

    #clear the screen
    screen.fill((0,0,0))

    # render the map
    for sprite_layer in sprite_layers:
      if sprite_layer.is_object_group:
      #we dont draw the object group layers
      # you should filter them out if not needed
        continue
      else:
        renderer.render_layer(screen, sprite_layer)

    pygame.display.flip()


def check_collision(hero_pos_x, hero_pos_y, step_x, step_y, \
                                    hero_width, hero_height, coll_layer):
  """
  Checks collision of the hero against the world. Its not the best way to
  handle collision detection but for this demo it is good enough.

  :Returns: steps to add to heros current position.
  """
  # create hero rect
  hero_rect = pygame.Rect(0, 0, hero_width, hero_height)
  hero_rect.midbottom = (hero_pos_x, hero_pos_y)

  # find the tile location of the hero
  tile_x = int((hero_pos_x) // coll_layer.tilewidth)
  tile_y = int((hero_pos_y) // coll_layer.tileheight)

  # find the tiles around the hero and extract their rects for collision
  tile_rects = []
  for diry in (-1, 0 , 1):
      for dirx in (-1, 0, 1):
          if coll_layer.content2D[tile_y + diry][tile_x + dirx] is not None:
              tile_rects.append(coll_layer.content2D[tile_y + diry][tile_x + dirx].rect)

  # save the original steps and return them if not canceled
  res_step_x = step_x
  res_step_y = step_y

  # x direction, floor or ceil depending on the sign of the step
  step_x = special_round(step_x)

  # detect a collision and dont move in x direction if colliding
  if hero_rect.move(step_x, 0).collidelist(tile_rects) > -1:
        res_step_x = 0

  # y direction, floor or ceil depending on the sign of the step
  step_y = special_round(step_y)

  # detect a collision and dont move in y direction if colliding
  if hero_rect.move(0, step_y).collidelist(tile_rects) > -1:
      res_step_y = 0

  # return the step the hero should do
  return res_step_x, res_step_y


def special_round(value):
  """
  For negative numbers it returns the value floored,
  for positive numbers it returns the value ceiled.
  """
  # same as:  math.copysign(math.ceil(abs(x)), x)
  # OR:
  # ## versus this, which could save many function calls
  # import math
  # ceil_or_floor = { True : math.ceil, False : math.floor, }
  # # usage
  # x = floor_or_ceil[val<0.0](val)

  if value < 0:
    return math.floor(value)
  return math.ceil(value)

 
